package com.test.miniotest.minio.controller;

import com.test.miniotest.minio.mapper.UploadDtoMapper;
import com.test.miniotest.minio.utils.MinIOUtil;
import com.test.miniotest.minio.utils.R;
import com.test.miniotest.minio.utils.UploadDto;
import com.test.miniotest.minio.utils.UuidUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@RequestMapping("/upload")
@RestController
public class BigFile2Controller {

    @Autowired
    private UploadDtoMapper uploadDtoMapper;

    /**
     * 上传成功
     */
    private final Integer UPLOAD_SUCCESS = 0;

    /**
     * 部分上传
     */
    private final Integer UPLOAD_PART = 1;


    @Autowired
    private MinIOUtil minIOUtil;

    /**
     * 大文件存储桶名称
     */
    @Value("${minio.bigFileBucketName}")
    private String bigFileBucketName;

    /**
     * 大文件分片文件存储桶名称
     */
    @Value("${minio.bigFileChunkBucketName}")
    private String bigFileChunkBucketName;


    /**
     * 初始化大文件上传
     * 未做优化，业务逻辑请根据实际情况修改
     *
     * @param uploadDto
     * @return
     */
    @PostMapping("init-chunk-upload")
    public R initChunkUpload(@RequestBody UploadDto uploadDto) throws Exception {
        System.out.println("- - - " + uploadDto.toString());
        //校验文件md5，该文件是否上传过
        UploadDto mysqlFileData = uploadDtoMapper.selectByMd5(uploadDto.getFileMd5());
        if (mysqlFileData != null) {
            //秒传
            if (UPLOAD_SUCCESS.equals(mysqlFileData.getUploadStatus())) {
                return R.ok(UPLOAD_SUCCESS, mysqlFileData);
            }
            //续传
            //获取到该文件已上传分片
            Map<Integer, String> okChunkMap = minIOUtil.mapChunkObjectNames(bigFileChunkBucketName, uploadDto.getFileMd5());
            List<UploadDto> chunkUploadUrls = new ArrayList<>();
            if (okChunkMap.size() > 0) {
                for (int i = 1; i <= uploadDto.getChunkCount(); i++) {
                    //判断当前分片是否已经上传过了
                    if (!okChunkMap.containsKey(i)) {
                        //生成分片上传url
                        UploadDto url = new UploadDto();
                        url.setPartNumber(i);
                        url.setUploadUrl(minIOUtil.createUploadChunkUrl(bigFileChunkBucketName, uploadDto.getFileMd5(), i));
                        chunkUploadUrls.add(url);
                    }
                }
                if (chunkUploadUrls.size() == 0) {
                    return R.ok("所有分片已经上传完成，仅需要合并文件");
                }
                return R.ok(mysqlFileData.getUploadStatus(), chunkUploadUrls);
            }
        }
        //初次上传和已有文件信息但未上传任何分片的情况下则直接生成所有上传url
        List<String> uploadUrls = minIOUtil.createUploadChunkUrlList(bigFileChunkBucketName, uploadDto.getFileMd5(), uploadDto.getChunkCount());
        List<UploadDto> chunkUploadUrls = new ArrayList<>();
        for (int i = 1; i <= uploadUrls.size(); i++) {
            UploadDto url = new UploadDto();
            url.setPartNumber(i);
            url.setUploadUrl(minIOUtil.createUploadChunkUrl(bigFileChunkBucketName, uploadDto.getFileMd5(), i));
            chunkUploadUrls.add(url);
        }
        //向数据库中记录该文件的上传信息
        uploadDto.setUploadStatus(UPLOAD_PART);
        uploadDto.setObjectId(UuidUtil.get32UUID());
        uploadDtoMapper.insertUploadDto(uploadDto);

        return R.ok(UPLOAD_PART, chunkUploadUrls);
    }

    /**
     * 合并文件并返回文件信息
     *
     * @param uploadDto
     * @return
     */
    @PostMapping("compose-file")
    public R composeFile(@RequestBody UploadDto uploadDto) throws Exception {
        //根据md5获取所有分片文件名称(minio的文件名称 = 文件path)
        List<String> chunks = minIOUtil.listObjectNames(bigFileChunkBucketName, uploadDto.getFileMd5());

        //将大文件分片合并后存储到指定桶
        uploadDto.setBucketName(bigFileBucketName);

        // 新的文件名 = 存储桶名称_时间戳.后缀名
        String objectName = uploadDto.getBucketName() + "_" + System.currentTimeMillis() + uploadDto.getFileName().substring(uploadDto.getFileName().lastIndexOf("."));


        try {
            //合并文件
            String s = minIOUtil.composeObject(bigFileChunkBucketName,bigFileBucketName, chunks, objectName);
            //获取数据库里记录的文件信息，修改数据并返回文件信息
            UploadDto dbData = uploadDtoMapper.selectByMd5(uploadDto.getFileMd5());
            dbData.setUploadStatus(UPLOAD_SUCCESS);
            dbData.setFileName(objectName);
            dbData.setFilePath(s);
            uploadDtoMapper.updateUploadDto(dbData);

            return R.ok(dbData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return R.failed("合并文件失败");

    }

}
